package com.example.demo.util;

import cn.hutool.json.JSONUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

/**
 * rabbitmq 工具类
 */
@Slf4j
@Component
public class RabbitMQTool {

    @Resource
    private RabbitAdmin rabbitAdmin;

    @Resource
    private RabbitTemplate rabbitTemplate;

    // ======================
    // 1. 动态声明队列
    // ======================
    public Queue declareQueue(String queueName, boolean durable) {
        Queue queue = new Queue(queueName, durable);
        rabbitAdmin.declareQueue(queue);
        log.info("【动态声明队列】queue = {}", queueName);
        return queue;
    }

    // ======================
    // 2. 动态声明交换机
    // ======================

    public FanoutExchange declareFanoutExchange(String exchangeName) {
        FanoutExchange exchange = new FanoutExchange(exchangeName);
        rabbitAdmin.declareExchange(exchange);
        log.info("【动态声明 Fanout Exchange】exchange = {}", exchangeName);
        return exchange;
    }

    public DirectExchange declareDirectExchange(String exchangeName) {
        DirectExchange exchange = new DirectExchange(exchangeName);
        rabbitAdmin.declareExchange(exchange);
        log.info("【动态声明 Direct Exchange】exchange = {}", exchangeName);
        return exchange;
    }

    public TopicExchange declareTopicExchange(String exchangeName) {
        TopicExchange exchange = new TopicExchange(exchangeName);
        rabbitAdmin.declareExchange(exchange);
        log.info("【动态声明 Topic Exchange】exchange = {}", exchangeName);
        return exchange;
    }

    /**
     * 声明延迟交换机（需要安装 rabbitmq-delayed-message-exchange 插件）
     * delayedType 通常为 "direct"、"topic" 或 "fanout"，表示底层路由行为
     */
    public CustomExchange declareDelayedExchange(String exchangeName, String delayedType) {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", delayedType);
        CustomExchange exchange = new CustomExchange(
                exchangeName,
                "x-delayed-message",
                true,
                false,
                args
        );
        rabbitAdmin.declareExchange(exchange);
        log.info("【动态声明 Delayed Exchange(插件)】exchange = {}, delayedType = {}", exchangeName, delayedType);
        return exchange;
    }

    // ======================
    // 3. 动态绑定
    // ======================

    public Binding bindFanout(Queue queue, FanoutExchange exchange) {
        Binding binding = BindingBuilder.bind(queue).to(exchange);
        rabbitAdmin.declareBinding(binding);
        log.info("【动态绑定 Fanout】queue={} -> exchange={}", queue.getName(), exchange.getName());
        return binding;
    }

    public Binding bindDirect(Queue queue, DirectExchange exchange, String routingKey) {
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey);
        rabbitAdmin.declareBinding(binding);
        log.info("【动态绑定 Direct】queue={} -> exchange={} with key={}", queue.getName(), exchange.getName(), routingKey);
        return binding;
    }

    public Binding bindTopic(Queue queue, TopicExchange exchange, String routingKey) {
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey);
        rabbitAdmin.declareBinding(binding);
        log.info("【动态绑定 Topic】queue={} -> exchange={} with key={}", queue.getName(), exchange.getName(), routingKey);
        return binding;
    }

    /**
     * 绑定延迟交换机（插件方式）到队列
     */
    public Binding bindDelayed(Queue queue, CustomExchange exchange, String routingKey) {
        Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
        rabbitAdmin.declareBinding(binding);
        log.info("【动态绑定 Delayed(插件)】queue={} -> exchange={} with key={}", queue.getName(), exchange.getName(), routingKey);
        return binding;
    }

    // ======================
    // 4. 动态发送消息
    // ======================

    /**
     * 发送到队列（简单模式 / 工作队列）
     */
    public void sendToQueue(String queueName, Object message) {
        rabbitTemplate.convertAndSend(queueName, JSONUtil.toJsonStr(message));
        log.info("【发送消息到队列】queue={}, message={}", queueName, JSONUtil.toJsonStr(message));
    }

    /**
     * 发送到交换机
     */
    public void sendToFanoutExchange(String exchangeName, Object message) {
        rabbitTemplate.convertAndSend(exchangeName, "", JSONUtil.toJsonStr(message)); // fanout 忽略 routingKey
        log.info("【发送消息到 Fanout Exchange】exchange={}, message={}", exchangeName, JSONUtil.toJsonStr(message));
    }

    public void sendToDirectExchange(String exchangeName, String routingKey, Object message) {
        rabbitTemplate.convertAndSend(exchangeName, routingKey, JSONUtil.toJsonStr(message));
        log.info("【发送消息到 Direct Exchange】exchange={}, routingKey={}, message={}", exchangeName, routingKey, JSONUtil.toJsonStr(message));
    }

    public void sendToTopicExchange(String exchangeName, String routingKey, Object message) {
        rabbitTemplate.convertAndSend(exchangeName, routingKey, JSONUtil.toJsonStr(message));
        log.info("【发送消息到 Topic Exchange】exchange={}, routingKey={}, message={}", exchangeName, routingKey, JSONUtil.toJsonStr(message));
    }

    // ======================
    // 5. 延时队列支持
    // ======================

    /**
     * 通过插件的延迟交换机发送延时消息（需要安装 rabbitmq-delayed-message-exchange）
     */
    public void sendToDelayedExchange(String exchangeName, String routingKey, Object message, long delayMillis) {
        rabbitTemplate.convertAndSend(exchangeName, routingKey, JSONUtil.toJsonStr(message), msg -> {
            msg.getMessageProperties().setHeader("x-delay", delayMillis);
            return msg;
        });
        log.info("【发送延时消息(插件)】exchange={}, routingKey={}, delayMs={}, message={}", exchangeName, routingKey, delayMillis, JSONUtil.toJsonStr(message));
    }

    /**
     * 声明一个延迟队列（TTL+DLX 方式，不依赖插件）。
     * 当消息在该队列中过期后，会被投递到 deadLetterExchange + deadLetterRoutingKey 指向的真实路由/队列。
     * 可选：为队列设置统一 TTL（queueTtlMillis），也可以不设置，改为发送时设置每条消息的 TTL。
     */
    public Queue declareDelayQueueWithDLX(
            String delayQueueName,
            String deadLetterExchange,
            String deadLetterRoutingKey,
            boolean durable,
            Long queueTtlMillis
    ) {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", deadLetterExchange);
        args.put("x-dead-letter-routing-key", deadLetterRoutingKey);
        if (queueTtlMillis != null && queueTtlMillis > 0) {
            args.put("x-message-ttl", queueTtlMillis);
        }
        Queue queue = new Queue(delayQueueName, durable, false, false, args);
        rabbitAdmin.declareQueue(queue);
        log.info("【动态声明 Delay Queue(DLX)】queue = {}, DLX = {}, DLK = {}, queueTTL = {}",
                delayQueueName, deadLetterExchange, deadLetterRoutingKey, queueTtlMillis);
        return queue;
    }

    /**
     * 发送到延迟队列（TTL+DLX 方式）。
     * 注意：需要先通过 {@link #declareDelayQueueWithDLX} 声明好延迟队列及其 DLX 指向。
     * 消息在延迟队列内等待 delayMillis 后过期，随后由 DLX 投递到真实队列/交换机。
     */
    public void sendToDelayQueue(String delayQueueName, Object message, long delayMillis) {
        rabbitTemplate.convertAndSend(delayQueueName, JSONUtil.toJsonStr(message), msg -> {
            msg.getMessageProperties().setExpiration(String.valueOf(delayMillis));
            return msg;
        });
        log.info("【发送延时消息(DLX)】delayQueue={}, delayMs={}, message={}", delayQueueName, delayMillis, JSONUtil.toJsonStr(message));
    }

}
